package com.example.chirdrenserver.Service.UserService.Impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.chirdrenserver.Constant.RedisConstant;
import com.example.chirdrenserver.Holder.UserHolder;
import com.example.chirdrenserver.Pojo.dto.BasePage;
import com.example.chirdrenserver.Pojo.dto.PostDTO;
import com.example.chirdrenserver.Pojo.dto.PostQueryDTO;
import com.example.chirdrenserver.Pojo.entity.Post;
import com.example.chirdrenserver.Pojo.entity.PostContentMessage;
import com.example.chirdrenserver.Pojo.entity.PostFileMessage;
import com.example.chirdrenserver.Service.FileService;
import com.example.chirdrenserver.Service.UserService.PostService;
import com.example.chirdrenserver.confg.CacheConfig;
import com.example.chirdrenserver.exception.BaseException;
import com.example.chirdrenserver.mapper.PostMapper;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;

/**
 * @Author: Yu
 * @CreateTime: 2025-07-03
 * @Description:
 */
@Service
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService {

    @Autowired
    FileService fileService;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    CacheConfig cacheConfig;

    @Autowired
    PostMapper postMapper;

    public static BloomFilter<Long> postBloomFilter = BloomFilter.create(Funnels.longFunnel(), 10000, 0.01);

    @Override
    public Collection<Post> getPostByUserId(Long userId) {
        List<Post> posts = list(new LambdaQueryWrapper<Post>().eq(Post::getUserId, userId));
        Set<Post> postSet = posts.stream().map(post -> BeanUtil.copyProperties(post, Post.class)).collect(Collectors.toSet());
        return postSet;
    }

    @Override
    public Collection<Post> getPost(BasePage basePage) {

        ReentrantReadWriteLock.ReadLock readLock = cacheConfig.getreadLock();
        ReentrantReadWriteLock.WriteLock writeLock = cacheConfig.getwriteLock();
        Long offset = (basePage.getPage() - 1) * basePage.getLimit();
        List<Post> cachedResult = new ArrayList<>();
        QueryWrapper<Post> wrapper = new QueryWrapper<Post>().gt("post_id", offset).orderByAsc("post_id").last("LIMIT " + basePage.getLimit()); // 手动添加LIMIT;
        List<Post> postsId = postMapper.selectList(wrapper);
        List<Long> ids = new ArrayList<>();
        for (Post post : postsId) {
            ids.add(post.getPostId());
        }
        // 3. 读锁处理（并发读）
        readLock.lock();
        try {
            for (Long id : ids) {
                String cacheKey = RedisConstant.POST_CACHE + id;

                // 3.1 先检查布隆过滤器
                if (!postBloomFilter.mightContain(id)) {
                    continue;
                }

                // 3.2 从缓存获取数据
                Post post = getFromCache(cacheKey, id);
                if (post != null) {
                    cachedResult.add(post);
                    continue;
                }
            }

            if (cachedResult.size()== ids.size()) {
                return cachedResult;
            }
        } finally {
            readLock.unlock();
        }

        // 4. 写锁处理（单线程写）
        writeLock.lock();
        try {
            // 4.1 再次检查缓存（防止其他线程已经写入）
            for (Long id : ids) {
                String cacheKey = RedisConstant.POST_CACHE + id;
                Post post = getFromCache(cacheKey, id);
                if (post != null) {
                    cachedResult.add(post);
                }
            }

            if (cachedResult.size()== ids.size()) {
                return cachedResult;
            }

            // 4.2 查询数据库
            List<Post> records = page(basePage.page()).getRecords();

            // 4.3 更新缓存和布隆过滤器
            for (Post post : records) {
                if (post == null) continue;

                // 更新布隆过滤器
                postBloomFilter.put(post.getPostId());

                // 写入Redis缓存
                String cacheKey = RedisConstant.POST_CACHE + post.getPostId();
                putToCache(cacheKey, post);

                cachedResult.add(post);
            }

            return cachedResult;
        } finally {
            writeLock.unlock();
        }
    }

    private Post getFromCache(String key, Long id) {
        Post post = (Post) redisTemplate.opsForValue().get(key);
        // 实际实现中使用Redis或Caffeine等
        return post;
    }

    private void putToCache(String key, Post post) {
        // 使用 JSON 序列化
        redisTemplate.opsForValue().set(key, post);
    }


    @Override
    public Collection<Post> getPostByTitle(PostQueryDTO postQueryDTO) {
        Set<Post> postSet = new HashSet<>();
        List<Post> records = page(postQueryDTO.page(), new LambdaQueryWrapper<Post>().like(Post::getTitle, postQueryDTO.getPostTitle())).getRecords();
        for (Post post : records) {
            if (!(post.getPostType() == 0) && (BeanUtil.isEmpty(fileService.getById(post.getFileId())))) {
                continue;
            }
            postSet.add(post);
        }
        return postSet;
    }

    @Override
    public boolean putPost(PostDTO postDTO) {
        Long UserId = (Long) UserHolder.get();
        Post post = new Post();
        BeanUtils.copyProperties(postDTO, post);
        post.setUserId(UserId);
        if (BeanUtil.isEmpty(fileService.getById(postDTO.getFileId()))) {
            throw new BaseException("文件不存在");
        }
        if (!save(post)) {
            throw new BaseException("上传失败");
        }
        return true;
    }

    @Override
    @Async
    public void addFilePost(PostFileMessage postFileMessage) {
        Post post = new Post();
        BeanUtils.copyProperties(postFileMessage, post);
        if (ObjectUtil.isEmpty(fileService.getById(post.getFileId()))) {
            throw new BaseException("文件不存在");
        }
        save(post);
    }

    @Override
    @Async
    public void addContent(PostContentMessage postContentMessage) {
        Post post = new Post();
        BeanUtils.copyProperties(postContentMessage, post);
        post.setFileId(Long.valueOf(0));
        save(post);

    }
}
